package main

import (
	"errors"
	"fmt"
)

/*定义接口*/
/*
   1. interface可以被任意的对象实现
   2. 一个对象可以实现任意多个interface
   3. 任意的类型都实现了空interface(我们这样定义：interface{})，也就是包含0个method的interface


   详见：$GOROOT/src/runtime/runtime2.go中找到接口类型变量在运行时的表示。


	// $GOROOT/src/runtime/runtime2.go
	type iface struct {
    	tab  *itab
    	data unsafe.Pointer
	}

	type eface struct {
   	 	_type *_type
    	data  unsafe.Pointer
	}

   接口类型变量有两种内部表示：iface和eface，这两种表示分别用于不同的接口类型变量：
   1、eface 用于表示没有方法的空接口（empty interface）类型变量，也就是 interface{}类型的变量。
   2、iface 用于表示其余拥有方法的接口 interface 类型变量。
   3、两个结构的共同点是它们都有两个指针字段，并且第二个指针字段的功能相同，都是指向当前赋值给该接口类型变量的动态类型变量的值

*/
type Phone interface {
	call()
}

type NokiaPhone struct {
}

type IPhone struct {
}

/*NokiaPhone 实现接口方法*/
func (n NokiaPhone) call() {
	fmt.Println("I am Nokia, I can call you!")
}

/*IPhone 实现接口方法*/
func (iphone IPhone) call() {
	fmt.Println("I am iphone, I can call you!")
}

type Human struct {
	name  string
	age   int
	phone string
}

type Student struct {
	Human  //匿名字段
	school string
	loan   float32
}
type Employee struct {
	Human   //匿名字段
	company string
	money   float32
}

/*Interface Men被Human,Student和Employee实现*/
type Men interface {
	sayHi()
	sing(lyrics string)
}

var ErrBad = UserError{
	error: errors.New("bad things happened! "),
}

func main() {

	var phone Phone
	//
	phone = new(NokiaPhone)
	phone.call()

	phone = new(IPhone)
	phone.call()

	// 接口的使用
	mike := Student{Human{"Mike", 25, "222-222-XXX"}, "MIT", 0.00}
	tom := Employee{Human{"Sam", 36, "444-222-XXX"}, "Things Ltd.", 5000}

	//定义Men类型的变量men
	var men Men
	fmt.Println("variable men storage Student type.........")
	// men能存储Student
	men = mike
	fmt.Println("This is Mike, a Student:")
	men.sayHi()
	men.sing("November rain")

	fmt.Println("variable men storage Employee type.........")
	//men也能存储Employee
	men = tom
	fmt.Println("This is Tom, an Employee:")
	men.sayHi()
	men.sing("Born to be wild")

	fmt.Println()

	fmt.Println("start : go duck type")

	animals := []QuackableAnimal{Duck{}, Dog{}, Bird{}}

	for _, animal := range animals {
		AnimalQuackInForest(animal)
	}

	fmt.Println("end : go duck type")

	fmt.Println()

	fmt.Println("start : nil error 值 != nil")

	// err := returnsError()
	// error occur : <nil>
	// 程序并未如预期的那样输出 rror.not.happened, it is ok。
	// 程序显然是进入了错误处理分支，输出了 err 的值。那这里就有一个问题了：明明 returnsError 函数返回的 p 值为 nil，为什么却满足了if err != nil的条件进入错误处理分支呢
	//if err != nil {
	//	fmt.Printf("error occur : %+v\n", err)
	//	return
	//}
	printNilInterface()
	fmt.Println("error.not.happened, it is ok")
	fmt.Println("end : nil error 值 != nil")

	fmt.Println()
	fmt.Println("start：空接口类型变量")
	printEmptyInterface()
	fmt.Println("end：空接口类型变量")

	fmt.Println()

	fmt.Println("start: 非空接口类型变量")
	printNonEmptyInterface()
	fmt.Println("end: 非空接口类型变量")

	fmt.Println()
	fmt.Println("start : 空接口类型变量与非空接口类型变量的等值比较")
	printEmptyInterfaceAndNonEmptyInterface()
	fmt.Println("end: 空接口类型变量与非空接口类型变量的等值比较")

	fmt.Println()

	// 将任意类型赋值给一个接口类型变量也是装箱操作, 接口类型的装箱实际就是创建一个 eface 或 iface 的过程
	fmt.Println("start: 接口类型的装箱（boxing）原理")
	var box = BoxType{
		n: 17,
		s: "hello, interface",
	}
	var ei interface{}

	ei = box

	var i NonEmptyInterface
	i = box

	fmt.Println(ei)
	fmt.Println(i)
	fmt.Println("end: 接口类型的装箱（boxing）原理")

}

func (h Human) sayHi() {
	fmt.Printf("Hi, I am %s you can call me on %s\n", h.name, h.phone)
}

func (h Human) sing(lyrics string) {
	fmt.Println("La la la la...", lyrics)
}

func (e Employee) sayHi() {
	fmt.Printf("Hi, I am %s, I work at %s. Call me on %s\n", e.name, e.company, e.phone)
}

// duck type
type QuackableAnimal interface {
	Quack()
}

type Duck struct{}

func (Duck) Quack() {
	println("duck quack!")
}

type Dog struct{}

func (Dog) Quack() {
	println("dog quack!")
}

type Bird struct{}

func (Bird) Quack() {
	println("bird quack!")
}

func AnimalQuackInForest(animal QuackableAnimal) {
	animal.Quack()
}

type UserError struct {
	error
}

func bad() bool {
	return false
}

// 从 returnsError 返回的 error 接口类型变量 err 的数据指针虽然为空，但它的类型信息（iface.tab）并不为空，
// 而是 *MyError 对应的类型信息，这样 err 与 nil（0x0,0x0）相比自然不相等
func returnsError() error {
	var p *UserError = nil
	if bad() {
		p = &ErrBad
	}
	return p
}

// 无论是空接口类型还是非空接口类型变量，一旦变量值为 nil，那么它们内部表示均为(0x0,0x0)，也就是类型信息、数据值信息均为空
func printNilInterface() {
	// nil接口变量
	var i interface{}                  // 空接口类型
	var err error                      // 非空接口类型
	println(i)                         // (0x0,0x0)
	println(err)                       // (0x0,0x0)
	println("i == nil", i == nil)      // i == nil true
	println("err == nil ", err == nil) // err == nil  true
	println("i == err", i == err)      // i == err true
}

// 对于空接口类型变量，只有 _type 和 data 所指数据内容一致的情况下，两个空接口类型变量之间才能划等号
func printEmptyInterface() {
	var eif1 interface{} // 空接口类型
	var eif2 interface{} // 空接口类型
	var n, m int = 17, 18

	eif1 = n
	eif2 = m

	println("eif1:", eif1)
	println("eif2:", eif2)
	println("eif1 = eif2:", eif1 == eif2) // false

	eif2 = 17
	println("eif1:", eif1)
	println("eif2:", eif2)
	println("eif1 = eif2:", eif1 == eif2) // true

	eif2 = int64(17)
	println("eif1:", eif1)
	println("eif2:", eif2)
	println("eif1 = eif2:", eif1 == eif2) // false
}

// 非空接口类型变量

type T int

func (t T) Error() string {
	return "bad error"
}

func printNonEmptyInterface() {
	var err1 error // 非空接口类型
	var err2 error // 非空接口类型
	err1 = (*T)(nil)
	println("err1:", err1)
	// println 输出的 err1 是（0x10ed120, 0x0），也就是非空接口类型变量的类型信息并不为空，数据指针为空，因此它与 nil（0x0,0x0）之间不能划等号
	println("err1 = nil:", err1 == nil)

	err1 = T(5)
	err2 = T(6)
	println("err1:", err1)
	println("err2:", err2)
	println("err1 = err2:", err1 == err2)

	err2 = fmt.Errorf("%d\n", 5)
	println("err1:", err1)
	println("err2:", err2)
	println("err1 = err2:", err1 == err2)
}

// 空接口类型变量和非空接口类型变量内部表示的结构有所不同（第一个字段：_type vs. tab)，两者似乎一定不能相等。
// 但 Go 在进行等值比较时，类型比较使用的是 eface 的 _type 和 iface 的 tab._type
func printEmptyInterfaceAndNonEmptyInterface() {
	var eif interface{} = T(5)
	var err error = T(5)
	println("eif:", eif)
	println("err:", err)
	println("eif = err:", eif == err)

	err = T(6)
	println("eif:", eif)
	println("err:", err)
	println("eif = err:", eif == err)
}

type BoxType struct {
	n int
	s string
}

type NonEmptyInterface interface {
	M1()
	M2()
}

func (BoxType) M1() {

}

func (BoxType) M2() {

}
